package vnu.edu;

import jade.content.AgentAction;
import jade.content.ContentElement;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.UngroundedException;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Result;
import jade.core.AID;
import jade.core.Agent;
import jade.core.ContainerID;
import jade.core.PlatformID;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.ParallelBehaviour;
import jade.core.behaviours.SequentialBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.FIPAManagementOntology;
import jade.domain.FIPAAgentManagement.Property;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.List;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import jade.wrapper.ContainerController;
import jade.wrapper.ControllerException;
import jade.wrapper.StaleProxyException;

import java.util.Date;

import javax.sound.midi.SysexMessage;

import vnu.edu.NewsAgent.ReceiveMessages;
import vnu.edu.NewsAgent.RegisterInDF;
import vnu.edu.vocabulary.NewsVocabulary;

public class NewsMobilityAgent extends Agent implements NewsVocabulary {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static final String WSIG_FLAG = "wsig";
	public static final String WSIG_MAPPER = "wsig-mapper";
	public static final String WSIG_PREFIX = "wsig-prefix";

	// private Logger log = Logger.getLogger(NewsMobilityAgent.class.getName());
	public static AID myAID = null;

	private SLCodec codec = new SLCodec();
	private Ontology newsOntology = NewsOntology.getInstance();

	private Date startDate;
	private boolean isOnProxy;
	private boolean isNewestNews;
	private List currentNewsList;
	private float technologyPriority;
	private float sportPriority;
	private String userName;

	private String databaseAgentAddress;
	private AID databaseAgentAID;

	MovingTickerBehaviour movingBehaviour;

	public static boolean isAcceptablePriority(float technology, float sport) {
		if (technology >= 0 && technology <= 10 && sport >= 0 && sport <= 10
				&& (technology + sport) <= 10) {
			return true;
		} else {
			return false;
		}
	}

	protected void setup() {
		super.setup();
		System.out.println("A NewsAgent is starting...");
		System.out.println("Agent name: " + getLocalName());

		// Get agent arguments
		Object[] args = getArguments();
		if (args.length >= 1) {
			databaseAgentAddress = (String) args[0];
			databaseAgentAID = new AID(databaseAgentAddress, AID.ISLOCALNAME);
		}
		startDate = new Date();
		isOnProxy = false;
		isNewestNews = false;
		currentNewsList = new ArrayList();
		technologyPriority = sportPriority = 0;

		System.out.println("A NewsAgent is started");

		// Add news behaviour
		SequentialBehaviour sb = new SequentialBehaviour();
		sb.addSubBehaviour(new RegisterInDF(this));
		sb.addSubBehaviour(new ReceiveMessages(this));
		addBehaviour(sb);
	}

	class RegisterInDF extends OneShotBehaviour {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		// --------------------------------------------- Register in the DF for
		// the client agent
		// be able to retrieve its AID
		RegisterInDF(Agent a) {
			super(a);
		}

		public void action() {
			// Register codec/onto
			getContentManager().registerLanguage(codec);
			getContentManager().registerOntology(
					FIPAManagementOntology.getInstance());
			getContentManager().registerOntology(newsOntology);

			// Prepare a DFAgentDescription
			DFAgentDescription dfad = new DFAgentDescription();
			dfad.setName(myAgent.getAID());
			dfad.addLanguages(codec.getName());
			dfad.addProtocols(FIPANames.InteractionProtocol.FIPA_REQUEST);

			ServiceDescription sd = new ServiceDescription();
			sd.addLanguages(codec.getName());
			sd.addProtocols(FIPANames.InteractionProtocol.FIPA_REQUEST);
			sd.setType(NEWS_AGENT_NAME);
			sd.setOwnership(NEWS_OWNERSHIP);
			sd.addOntologies(NewsOntology.getInstance().getName());

			// WISG properties
			sd.addProperties(new Property(WSIG_FLAG, "true"));

			// Service name
			String wsigServiceName = NEWS_SERVICE_NAME;
			System.out.println("Service name:" + wsigServiceName);
			sd.setName(wsigServiceName);

			dfad.addServices(sd);
			// DF registration
			try {
				DFService.register(myAgent, dfad);
			} catch (Exception e) {
				// TODO: handle exception
				System.err.println("Problem during DF registration " + e);
				doDelete();
			}
			// sendRequestToDBAgent();
		}

		@Override
		public int onEnd() {
			// TODO Auto-generated method stub
			if (!isNewestNews && !isOnProxy) {
				sendRequestToDBAgent();
			}
			return super.onEnd();
		}
	}

	class ReceiveMessages extends CyclicBehaviour {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public ReceiveMessages(Agent a) {
			super(a);
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			ACLMessage receivedMsg = receive(MessageTemplate
					.not(MessageTemplate.MatchSender(databaseAgentAID)));
			if (receivedMsg == null) {
				block();
				return;
			}
			Action actExpr;
			try {
				actExpr = (Action) myAgent.getContentManager().extractContent(
						receivedMsg);
				AgentAction action = (AgentAction) actExpr.getAction();
				int msgPerformative = receivedMsg.getPerformative();
				switch (msgPerformative) {
				case ACLMessage.REQUEST: {
					System.out.println("Request from: "
							+ receivedMsg.getSender().getLocalName());
					if (action instanceof NewsMaker) {
						System.out.println("Action NewsMaker");
						userName = ((NewsMaker) action).getUserNameElement();
						technologyPriority = ((NewsMaker) action)
								.getTechnologyPriorityElement();
						sportPriority = ((NewsMaker) action)
								.getSportPriorityElement();
						if (!isNewestNews) {
							sendRequestToDBAgent();
						}
						if (!isOnProxy) {
							sendInsertRequestInformationToDBAgent(userName);
						}
						if (userName.equalsIgnoreCase("proxy") && !isOnProxy) {
							movingBehaviour = new MovingTickerBehaviour(
									myAgent, 1000);
							SequentialBehaviour sq = new SequentialBehaviour();
							sq.addSubBehaviour(new HandleNewsMaker(myAgent,
									receivedMsg));
							sq.addSubBehaviour(movingBehaviour);
							addBehaviour(sq);
						} else {
							addBehaviour(new HandleNewsMaker(myAgent,
									receivedMsg));
						}
					} else {
						System.out.println("Another action");
						replyNotUnderstood(receivedMsg);
					}
				}
					break;
				case ACLMessage.INFORM: {
					System.out.println("Inform from: "
							+ receivedMsg.getSender().getLocalName());
				}
					break;
				default:
					break;
				}

			} catch (Exception e) {
				// TODO: handle exception
				System.err.println("Exception: " + e.getMessage() + e);
				// log.error("Exception: " + e.getMessage(), e);
			}
		}
	}

	class HandleNewsMaker extends OneShotBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private ACLMessage request;

		HandleNewsMaker(Agent a, ACLMessage request) {
			super(a);
			this.request = request;
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			try {
				ContentElement content = getContentManager().extractContent(
						request);
				// NewsMaker newsMaker =
				// (NewsMaker)((Action)content).getAction();
				Result result;
				if (NewsMobilityAgent.isAcceptablePriority(technologyPriority,
						sportPriority)) {
					List respondList = new ArrayList();
					for (int i = 0; i < technologyPriority; i++) {
						NewsElement newsElement = (NewsElement) currentNewsList
								.get(i);
						respondList.add(newsElement);
					}
					for (int i = 0; i < sportPriority; i++) {
						NewsElement newsElement = (NewsElement) currentNewsList
								.get(10 + i);
						respondList.add(newsElement);
					}
					result = new Result((Action) content, respondList);
				} else {
					result = new Result((Action) content, currentNewsList);
				}
				ACLMessage reply = request.createReply();
				reply.setPerformative(ACLMessage.INFORM);
				getContentManager().fillContent(reply, result);
				send(reply);
			} catch (UngroundedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (CodecException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (OntologyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	class WaitDataBaseAgentRespond extends ParallelBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		WaitDataBaseAgentRespond(Agent a) {
			super(a, 1);
			addSubBehaviour(new ReceiveDataResponse(myAgent));
			addSubBehaviour(new WakerBehaviour(myAgent, 15000) {

				/**
				 * 
				 */
				private static final long serialVersionUID = 1L;

				protected void handleElapsedTimeout() {
					System.err
							.println("Can not received any responds from DataBase Agent!");
				}
			});
		}

	}

	class ReceiveDataResponse extends SimpleBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private boolean isFinished = false;

		public ReceiveDataResponse(Agent a) {
			// TODO Auto-generated constructor stub
			super(a);
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			ACLMessage receivedDataMsg = receive(MessageTemplate
					.MatchSender(databaseAgentAID));
			if (receivedDataMsg == null) {
				block();
				return;
			}
			int receivedPerformative = receivedDataMsg.getPerformative();
			if (receivedPerformative == ACLMessage.NOT_UNDERSTOOD) {
				System.out
						.println("DataBaseAgent can not understood newsAgent's request!");
			} else if (receivedPerformative == ACLMessage.INFORM) {
				System.out
						.println("Received INFORM message from database Agent");

				try {
					ContentElement content = getContentManager()
							.extractContent(receivedDataMsg);
					if (content instanceof Result) {
						Result receivedResult = (Result) content;
						if (receivedResult.getValue() instanceof List) {
							List updateNewsList = receivedResult.getItems();
							if (updateNewsList.size() > 0) {
								currentNewsList.clear();
								System.out.println("Updating newsElement");
								for (Iterator it = updateNewsList.iterator(); it
										.hasNext();) {
									currentNewsList
											.add((NewsElement) it.next());
								}
								isNewestNews = true;
							}

						} else {
							System.err
									.println("Received unexpected respond from database Agent");
						}
					}

				} catch (UngroundedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (CodecException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (OntologyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
			isFinished = true;
		}

		@Override
		public boolean done() {
			// TODO Auto-generated method stub
			return isFinished;
		}
	}

	protected void beforeMove() {
		System.out.println("beforeMove..." + getName());
	}

	protected void afterMove() {
		System.out.println("afterMove...");
		// this.removeBehaviour(movingBehaviour);
		isOnProxy = true;
		addBehaviour(new RegisterInDF(this));
	}

	protected void takeDown() {
		// deregister itself from the DF
		try {
			DFService.deregister(this);
		} catch (Exception e) {
			System.err.println(e);
		}
		System.out.println("A NewsAgent is taken down now.");
	}

	void sendRequestToDBAgent() {
		if (databaseAgentAID == null) {
			System.err.println("Cannot find DataBase Agent");
			return;
		}
		UpdateNews updateAction = new UpdateNews();
		updateAction.setFromDate(new Date());

		ACLMessage message = new ACLMessage(ACLMessage.REQUEST);
		message.setLanguage(codec.getName());
		message.setOntology(newsOntology.getName());
		try {
			getContentManager().fillContent(message,
					new Action(databaseAgentAID, updateAction));
			message.addReceiver(databaseAgentAID);
			send(message);
			addBehaviour(new WaitDataBaseAgentRespond(this));
			System.out.println("Sending request to DataBase Agent...");
		} catch (CodecException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (OntologyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return;
	}

	void sendInsertRequestInformationToDBAgent(String userRequestedName) {
		if (databaseAgentAID == null) {
			System.err.println("Cannot find DataBase Agent");
			return;
		}
		InsertRequestedInformationAction insertAction = new InsertRequestedInformationAction();
		insertAction.setUserRequested(userRequestedName);

		ACLMessage message = new ACLMessage(ACLMessage.REQUEST);
		message.setLanguage(codec.getName());
		message.setOntology(newsOntology.getName());
		try {
			getContentManager().fillContent(message,
					new Action(databaseAgentAID, insertAction));
			message.addReceiver(databaseAgentAID);
			send(message);
			System.out
					.println("Sending request to DataBase Agent for inserting...");
		} catch (CodecException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (OntologyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return;
	}

	void replyNotUnderstood(ACLMessage msg) {
		try {
			ContentElement content = getContentManager().extractContent(msg);
			ACLMessage reply = msg.createReply();
			reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
			getContentManager().fillContent(reply, content);
			send(reply);
			System.out.println("Not understood receivedMessage");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private class MovingTickerBehaviour extends TickerBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Agent agent;
		private int counter;

		public MovingTickerBehaviour(Agent a, long period) {
			super(a, period);
			// TODO Auto-generated constructor stub
			this.agent = a;
			counter = 0;
		}

		@Override
		protected void onTick() {
			// TODO Auto-generated method stub
			if (counter <= 10) {
				System.out.println(counter++);
			}
			if (counter == 22) {
				ContainerID cloneToContainer = new ContainerID();
				cloneToContainer.setName("Main-Container");
				myAgent.doClone(cloneToContainer, "cloneAgent");
			}
			if (myAgent.getName().equalsIgnoreCase("cloneAgent@Thai-PC")) {
				// agent.removeBehaviour(movingBehaviour);
				return;
			}
			if (counter == 22) {
				Object[] args = new Object[1];
				args[0] = databaseAgentAddress;
				AgentContainer aContainer = myAgent.getContainerController();
				try {
					AgentController aController = aContainer.createNewAgent(
							"cloneAgent", NewsMobilityAgent.class.getName(),
							args);
					aController.start();
				} catch (StaleProxyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			if (counter == 3) {
				AID remoteAMS = new AID("amm@iNIL", AID.ISGUID);
				remoteAMS.addAddresses("http://iNIL:7778/acc");
				PlatformID destination = new PlatformID(remoteAMS);
				agent.doMove(destination);
				isOnProxy = true;
			}
		}
	}
}
